﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Spider.Framework;
using Spider.Framework.DataAccess;
using Spider.Framework.Download;
using Spider.Framework.Mongo.Bson;
using Spider.Framework.Mongo.Driver.Builders;
using Spider.Framework.ObjectBase;
using Spider.Framework.ParserExpress;
using Spider.Framework.ProjectEngine.Business;
using Spider.Framework.ProjectEngine.Entity;
using Spider.Framework.UtilityCommon;
using Spider.Framework.UtilityCommon.Extensions;

namespace DataPersonalization.Utis
{
    public class ParserObject
    {
        #region Fields
        private const string SOLUTIONID = "50cff2b2a5191416a07058cc";
        private const string CONNECTION_STRING = "mongodb://binhnt:7584ab@ds027618.mongolab.com:27618/spider";
        private static readonly Dictionary<string, AcParser> _DictAcParser = new Dictionary<string, AcParser>();
        #endregion

        public static void LoadConfig()
        {
            // Hardcode
            var solutionId = new ObjectId(SOLUTIONID);
            Settings.MongoConnectionString = CONNECTION_STRING;

            // Load TemplateField            
            if (StoreList.ListTemplateField.IsNullOrEmpty())
                StoreList.LoadCommon(solutionId);

            // Load Parser
            //if (_ListAcParser.IsNullOrEmpty())
            //{
            //    var query = Query.EQ("SolutionId", solutionId);
            //    _ListAcParser = (new AcParserBO()).SelectList(query) ?? new List<AcParser>();
            //}
        }

        public static Result Parser(string link)
        {
            // Get Project Name
            if (!link.StartsWithExtend("http://")) link = "http://" + link;
            var domain = Utility.HtmlUtility.ToDomain(link);

            // Get Config
            var parser = LoadConfig(domain);
            if (parser == null) return new Result { Type = ResultType.ErrorConfig };

            // Download
            var project = new Project
            {
                EncodingType = (int)EncodingType.UTF8,
            };
            var linkEx = new LinkExtend
            {
                Link = link,
                LinkParent = string.Empty,
            };
            linkEx.HtmlContent = DownloadExpress.Download(linkEx, project);
            if (linkEx.HtmlContent.IsStringNullOrEmpty())
                return new Result { Type = ResultType.ErrorDownload };

            // Parser
            var obj = (new ParserDataExpress(parser)).ParserData(linkEx);
            if (obj == null) return new Result { Type = ResultType.ErrorParser };

            // Parser Data
            var objDto = ParserDataToObject<NewsEntity>(new ResultAction { Result = obj });
            ParserDataStandard(objDto);
            if (!objDto.Title.IsStringNullOrEmpty() &&
                !objDto.Content.IsStringNullOrEmpty() &&
                !objDto.Link.IsStringNullOrEmpty())
            {
                objDto.Content = objDto.Content.ReplaceTags();
                return new Result { Entity = objDto, Type = ResultType.Success };
            }
            return new Result { Type = ResultType.ErrorParser };
        }

        #region Utility
        private static AcParser LoadConfig(string domain)
        {
            AcParser entity;
            if (!_DictAcParser.TryGetValue(domain, out entity))
            {
                var query = Query.And(Query.EQ("SolutionId", new ObjectId(SOLUTIONID)), Query.EQ("Name", domain));
                entity = (new AcParserBO()).SelectOne(query);
                if (entity != null && !entity.ListDomain.IsNullOrEmpty())
                {
                    foreach (var item in entity.ListDomain)
                    {
                         if (!_DictAcParser.ContainsKey(item))   
                             _DictAcParser.Add(item, entity);
                    }
                }
                if (!_DictAcParser.ContainsKey(domain))
                    _DictAcParser.Add(domain, entity);
            }
            return entity;
        }

        protected static T ParserDataToObject<T>(ResultAction objResult)
        {
            // Init ObjectSave
            var objDto = (T)Activator.CreateInstance(typeof(T));

            // Parser Data
            ParserDataDicToObject(objResult.Result.Dictionary, objDto);

            // Url
            var pNews = typeof(T).GetProperty("UrlSource") ?? typeof(T).GetProperty("Link");
            if (pNews != null && !objResult.Result.LinkExtend.Link.IsStringNullOrEmpty())
                Utility.SetProperty(pNews, objResult.Result.LinkExtend.Link, objDto);

            // Keyword
            pNews = typeof(T).GetProperty("Keyword");
            if (pNews != null && !objResult.Result.LinkExtend.Keyword.IsStringNullOrEmpty())
            {
                var value = pNews.GetValue(objDto, null) as string;
                if (value.IsStringNullOrEmpty())
                    Utility.SetProperty(pNews, objResult.Result.LinkExtend.Keyword, objDto);
            }

            // Tags
            pNews = typeof(T).GetProperty("Tags");
            if (pNews != null && !objResult.Result.LinkExtend.Keyword.IsStringNullOrEmpty())
            {
                var value = pNews.GetValue(objDto, null) as string;
                if (value.IsStringNullOrEmpty())
                    Utility.SetProperty(pNews, objResult.Result.LinkExtend.Tags, objDto);
            }

            return objDto;
        }

        protected static void ParserDataDicToObject<T>(IDictionary<ObjectId, object> dicResult, T objDto)
        {
            if (dicResult == null || dicResult.Count <= 0) return;

            foreach (var item in StoreList.ListTemplateField
                .Where(c => !c.FieldMap.IsStringNullOrEmpty()))
            {
                object value;
                if (!dicResult.TryGetValue(item.Id, out value)) continue;
                var pNews = typeof(T).GetProperty(item.FieldMap);
                if (value is ObjectResult.ItemResultList &&
                    pNews.PropertyType.IsGenericType &&
                    pNews.PropertyType.GetGenericArguments().Length > 0)
                {
                    var argType = pNews.PropertyType.GetGenericArguments()[0];
                    var arrValue = (ObjectResult.ItemResultList)value;

                    var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(argType));
                    foreach (var itemValue in arrValue.ListDicField)
                    {
                        if (argType == typeof(string))
                            list.Add(itemValue.ElementAt(0).Value);
                        else if (argType == typeof(int))
                            list.Add(itemValue.ElementAt(0).Value.ToInt32());
                        else
                        {
                            var objType = Activator.CreateInstance(argType);
                            ParserDataDicToObject(itemValue, objType);
                            list.Add(objType);
                        }
                    }
                    Utility.SetProperty(pNews, list, objDto);
                }
                else
                {
                    Utility.SetProperty(pNews, value, objDto);
                }
            }
        }

        protected static void ParserDataDicToObject<T>(IDictionary<ObjectId, string> dicResult, T objDto)
        {
            if (dicResult == null || dicResult.Count <= 0) return;

            foreach (var item in StoreList.ListTemplateField
                .Where(c => !c.FieldMap.IsStringNullOrEmpty()))
            {
                string value;
                if (!dicResult.TryGetValue(item.Id, out value)) continue;

                var pNews = typeof(T).GetProperty(item.FieldMap) ?? objDto.GetType().GetProperty(item.FieldMap);
                Utility.SetProperty(pNews, value, objDto);
            }
        }

        private static void ParserDataStandard(NewsEntity entity)
        {
            // DateTime
            if (entity.DateTime.IsDateTimeNull())
                entity.DateTime = DateTime.Now;

            // Title
            entity.Title = Utility.ConvertUtility.ToUtf8(entity.Title);

            // Summury
            entity.Summury = Utility.ConvertUtility.ToUtf8(entity.Summury);
            if (!entity.Summury.IsStringNullOrEmpty())
            {
                entity.Summury = Regex.Replace(entity.Summury, @"^\(.*?\)", string.Empty);
                entity.Summury = Regex.Replace(entity.Summury, @"^\w{1,10}[\s]*(-|–)", string.Empty);
                entity.Summury = entity.Summury.Trim('–', '-', ' ');
            }

            // Image
            if (entity.Image.IsStringNullOrEmpty())
                entity.Image = entity.Content.GetElement(Constant.StringRegexImage, true);
        }
        #endregion

        #region Class Internal
        public class Result
        {
            public NewsEntity Entity { get; set; }
            public ResultType Type { get; set; }
        }
        public enum ResultType
        {
            Success = 1,
            ErrorConfig,
            ErrorDownload,
            ErrorParser
        }
        public class NewsEntity
        {
            public string Link { get; set; }
            public string Title { get; set; }
            public string Image { get; set; }
            public string Author { get; set; }
            public string Summury { get; set; }
            public string Content { get; set; }
            public DateTime DateTime { get; set; }
        }
        #endregion
    }

    
}
